
import os
import sys
import time
import types
import cPickle
import traceback
from threading import Thread

import rpyc
from rpyc.utils.server import ThreadedServer

from sqlalchemy import create_engine, Binary, Column, Integer, String, Boolean,\
                       MetaData, Table, Float
from sqlalchemy.types import type_map
from sqlalchemy.sql import select, or_, func
from sqlalchemy.orm import sessionmaker, query
from sqlalchemy.databases.sqlite import ischema_names, SLFloat

temp_dir = os.path.abspath(__file__)
for i in range(4):
    temp_dir = os.path.dirname(temp_dir)
sys.path.append(temp_dir)
from dexen.system import constants, exceptions 
from dexen.system.server.db_tables import Base, Schema, ScriptSettings,\
                                          PopulationSettings, ColumnSettings


""" MODIFIES DEFAULT BEHAVIOUR OF SQLALCHEMY """
ischema_names['FLOAT'] = SLFloat
type_map[float] = Float
""" ------------------------------------------"""

""" GLOBALS """
python_to_sql_type = {}
python_to_sql_type[constants.BOOLEAN] = Boolean
python_to_sql_type[constants.STRING] = String
python_to_sql_type[constants.FLOAT] = Float
python_to_sql_type[constants.OBJ] = Binary                
python_to_sql_type[constants.INT] = Integer

settings_engine = None
scripts_engine = None
SettingsSession = None
ScriptsSession = None

#TODO: Document the **kwargs.
class BaseService(rpyc.Service):
    
    def on_connect(self):
        self.settings_session = SettingsSession()
        self.scripts_session = ScriptsSession()           
        #print "I am in BaseService on_connect"
            
    def initialize(self, schema_name=None, pop_name=None, script_name=None):
        self.schema_name = schema_name
        self.pop_name = pop_name
        self.script_name = script_name
        self.pop_settings = self.get_population_obj()
        self.max_individuals = self.pop_settings.max_individuals
        self.script_settings = self.get_script_obj()
        self.ind_table = self.load_ind_table()   
        self.data_structure = self.get_data_structure() 
            
    def load_ind_table(self, schema_name=None, pop_name=None):
        print "I am in load_ind_table"
        metadata = MetaData()
        metadata.bind = scripts_engine
        
        if not schema_name: schema_name = self.schema_name
        if not pop_name: pop_name = self.pop_name
        
        ind_table = Table(schema_name + "_" + pop_name, metadata, autoload=True)
        
        return ind_table
        
    def get_population_obj(self, schema_name=None, pop_name=None):
        session = self.settings_session

        if not schema_name: schema_name = self.schema_name
        if not pop_name: pop_name = self.pop_name
        
        pop_obj = session.query(PopulationSettings).\
                            filter(PopulationSettings.name == pop_name).\
                            join(Schema).\
                            filter(Schema.name == schema_name).first()
        return pop_obj       
    
    def get_script_obj(self, schema_name=None, pop_name=None, script_name=None):
        session = self.settings_session

        if not schema_name: schema_name = self.schema_name
        if not pop_name: pop_name = self.pop_name
        if not script_name: script_name = self.script_name
        
        script_obj = session.query(ScriptSettings).\
                                    join(PopulationSettings).\
                                    join(Schema).\
                                    filter(Schema.name == schema_name).\
                                    filter(PopulationSettings.name == pop_name).\
                                    filter(ScriptSettings.name == script_name).first()
        return script_obj  

    def get_data_structure(self):
        session = self.settings_session
        schema_name = self.schema_name
        pop_name = self.pop_name
        data_structure = {}
        
        col_pairs = session.query(ColumnSettings.column_name, ColumnSettings.column_type).\
                            join(PopulationSettings).\
                            filter(PopulationSettings.name == pop_name).\
                            join(Schema).\
                            filter(Schema.name == schema_name).all()      
           
        for pair in col_pairs:
            data_structure[pair[0]] = pair[1]
        session.commit()
        return data_structure

    
class ConfigService(BaseService):    
    
    def on_connect(self):
        BaseService.on_connect(self)
        print "Somebody connected in ConfigService"
    
    def on_disconnect(self):
        pass
    
    def exposed_initialize(self, schema_name, pop_name):
        self.schema_name = schema_name
        self.pop_name = pop_name
        
    """ Later might convert to join """
    def exposed_setImage(self, image_name):
        session = self.settings_session
        pop_settings = self.get_population_obj()
        pop_settings.image_name = image_name        
        session.commit()

    def exposed_setModel(self, model_name):
        session = self.settings_session
        pop_settings = self.get_population_obj()
        pop_settings.model_name = model_name        
        session.commit()
    
    def exposed_setMaxIndividuals(self, max_individuals):
        session = self.settings_session
        pop_settings = self.get_population_obj()
        pop_settings.max_individuals = max_individuals            
        session.commit()          
    
    def exposed_setScript(self, script_name, file_name, function_name, run_type,
                          select=None, where=None, count=None, order_by=None, 
                          exclusive_use=False):
        session = self.settings_session
        pop_settings = self.get_population_obj()
        script_settings = ScriptSettings(script_name, file_name, function_name, 
                                         run_type, select, where, count, 
                                         order_by, exclusive_use)
        script_settings.population_settings = pop_settings
        session.add(script_settings)            
        session.commit()
       
    def exposed_setDataStructure(self, raw_attributes):
        try:
            session = self.settings_session
            attributes = cPickle.loads(raw_attributes)
            pop_settings = self.get_population_obj()
            for pair in attributes.items():
                col_settings = ColumnSettings(pair[0], pair[1])
                col_settings.population_settings = pop_settings
                session.add(col_settings)
            session.commit()
        except:
            tb = sys.exc_traceback
            print "Exception in setDataStrucutre at line ", traceback.tb_lineno(tb), "\n"
 
    def exposed_setGlobalData(self, raw_data):
        session = self.settings_session
        data = cPickle.loads(raw_data)
        pop_settings = self.get_population_obj()
        pop_settings.global_data = data
        session.commit()


class FrontendService(BaseService):
    def on_connect(self):
        print "Somebody connected in FrontendService"
        BaseService.on_connect(self)
        
    def on_disconnect(self):
        pass

    #def __init__(self):
    #    self.on_connect()

    def exposed_reset_all_databases(self):
        metadata = MetaData()
        metadata.reflect(scripts_engine)
        metadata.drop_all(scripts_engine)    
        Base.metadata.drop_all(settings_engine)
        Base.metadata.create_all(settings_engine)
    
    def exposed_reset_configdb(self):
        pass
    
    def exposed_set_individual_table(self, schema_name, pop_name):
        session = self.settings_session 
        metadata = MetaData()
        metadata.bind = scripts_engine
        
        ind_table_name = schema_name + "_" + pop_name
        ind_table = Table(ind_table_name, metadata,
                          Column('id', Integer, primary_key=True)
                    )
        
        col_pairs = session.query(ColumnSettings.column_name, ColumnSettings.column_type).\
                        join(PopulationSettings).\
                        filter(PopulationSettings.name == pop_name).\
                        join(Schema).\
                        filter(Schema.name == schema_name).all()
                                                      
        """ ADDING COLUMNS DEFINED BY setDataStructure() """
        for pair in col_pairs:
            ind_table.append_column(Column(pair[0], python_to_sql_type[pair[1]]))
        
        scripts = session.query(ScriptSettings).join(PopulationSettings).\
                        filter(PopulationSettings.name == pop_name).\
                        join(Schema).\
                        filter(Schema.name == schema_name).all()
        
        """ TIMESTAMPS FOR THE SCRIPTS """
        for script in scripts:
            ind_table.append_column(Column(script.name, Float))
            ind_table.append_column(Column(script.name+"_ts", Integer))
        
        """ ADDING THE DEAD/ALIVE STATE """
        ind_table.append_column(Column(constants.LIFE_STATE, Integer))
        ind_table.append_column(Column(constants.BIRTH_TS, Integer))
        ind_table.append_column(Column(constants.DEATH_TS, Integer))
        
        
        ind_table.append_column(Column(constants.IMAGE, Binary))
        ind_table.append_column(Column(constants.MODEL, Binary))
            
        ind_table.create(bind=scripts_engine)
        session.commit()  
            
    def exposed_set_schema(self, schema_name, zip_data, last_modified_time):
        session = self.settings_session
        schema = Schema(schema_name, zip_data, last_modified_time)
        session.add(schema)
        session.commit()
        
    def exposed_get_schemas(self):
        session = self.settings_session
        schemas = []
        for schema in session.query(Schema.name, Schema.zip_data, Schema.last_modified_time):
            schemas.append(schema)
        session.commit()
        return schemas 
    
    def exposed_get_schema(self, schema_name):
        session = self.settings_session
        schema = session.query(Schema.zip_data, Schema.last_modified_time).\
                        filter(Schema.name == schema_name).first()
        session.commit()
        return schema
    
    def exposed_get_schema_timestamp(self, schema_name):
        session = self.settings_session
        schema_timestamp = session.query(Schema.last_modified_time).\
                        filter(Schema.name == schema_name).scalar()
        session.commit()
        return schema_timestamp
    
    def exposed_get_population_creation_time(self, schema_name, pop_name):
        pop_settings = self.get_population_obj(schema_name, pop_name)
        return pop_settings.creation_time
    
    def exposed_get_schema_names(self):
        session = self.settings_session
        schema_names = []
        for schema_name in session.query(Schema.name).order_by(Schema.name):
            schema_names.append(schema_name[0])
        session.commit()
        return schema_names
    
    def exposed_stop_population(self, schema_name, pop_name):
        session = self.settings_session
        pop_settings = self.get_population_obj(schema_name, pop_name)
        pop_settings.is_stopped = True
        session.commit()    
    
    def exposed_delete_population(self, schema_name, pop_name):
        session = SettingsSession()
        pop_settings = self.get_population_obj(schema_name, pop_name)
        session.delete(pop_settings)
        
        # put load ind table
        
        session.commit()
    
    def exposed_stop_script(self, schema_name, pop_name, script_name):
        print "I am stopping the script:", script_name, "*****************************************"

        script_obj = self.get_script_obj(schema_name, pop_name, script_name)
        session = self.settings_session
        script_obj.is_stopped = True
        session.commit()
        #script_obj = self.getScriptObj(schema_name, pop_name, script_name)
        #print script_obj
    
    def exposed_set_population(self, schema_name, pop_name, creation_time):
        session = self.settings_session
        pop_settings = PopulationSettings(pop_name)
        pop_settings.creation_time = creation_time
        schema = session.query(Schema).filter(Schema.name == schema_name).first()
        pop_settings.schema = schema
        session.add(pop_settings)
        session.commit()

    
    def exposed_get_population_names(self, schema_name):
        session = self.settings_session
        schema = session.query(Schema).filter(Schema.name == schema_name).first()
        pop_names = []
        for pop_settings in schema.populations:
            pop_names.append(pop_settings.name)
        session.commit()
        return pop_names   

    """ Is pop name unique globally ?"""
    def exposed_get_script_file_names(self, schema_name, pop_name):
        session = self.settings_session
        script_settings = session.query(ScriptSettings).join(PopulationSettings).\
                                join(Schema).\
                                filter(Schema.name == schema_name).\
                                filter(PopulationSettings.name == pop_name).all()
        script_file_names = []
        #print script_settings        
        for script in script_settings:
            script_file_names.append(script.file_name)
        session.commit()
        return script_file_names
    
    def exposed_get_script_function_names(self, schema_name, pop_name, file_name):
        session = self.settings_session
        script_settings = session.query(ScriptSettings).\
                                join(PopulationSettings).\
                                join(Schema).\
                                filter(Schema.name == schema_name).\
                                filter(PopulationSettings.name == pop_name).\
                                filter(ScriptSettings.file_name == file_name).all()
        
        script_function_names = []
        for script in script_settings:
            script_function_names.append(script.function_name)
        session.commit()
        return script_function_names
    
    def exposed_get_script_file_function_names(self, schema_name, pop_name, script_name):
        session = self.settings_session        
        file_name, function_name = session.query(ScriptSettings.file_name, ScriptSettings.function_name).\
                                    join(PopulationSettings).\
                                    join(Schema).\
                                    filter(Schema.name == schema_name).\
                                    filter(PopulationSettings.name == pop_name).\
                                    filter(ScriptSettings.name == script_name).first()
        session.commit()                                              
        return file_name, function_name                             
    
    def exposed_get_script_names(self, schema_name, pop_name):
        session = self.settings_session
        script_settings = session.query(ScriptSettings).\
                                    join(PopulationSettings).\
                                    join(Schema).\
                                    filter(Schema.name == schema_name).\
                                    filter(PopulationSettings.name == pop_name).all()
        script_names = []
        
        for script in script_settings:
            script_names.append(script.name)
        session.commit()
        return script_names
    
    def exposed_get_script_run_type(self, schema_name, pop_name, script_name):
        script_settings = self.get_script_obj(schema_name, pop_name, script_name)
        return script_settings.run_type

class IndividualService(BaseService):  
    
    def on_connect(self):
        BaseService.on_connect(self)
    
    def initialize(self, schema_name, pop_name, script_name):
        BaseService.initialize(self, schema_name, pop_name, script_name)
    
    def update_individual(self, individual):
        session = self.scripts_session
        script_name = self.script_name
        ind_table = self.ind_table
        
        while True:
            try:
                """ This is to reset the EXCLUSIVE_USE """
                individual[script_name] = None
                session.execute(ind_table.update().where(ind_table.c.id == individual['id']), individual)
                break
            except:
                print script_name, ": exception in update individual"
                print sys.exc_info()
                session.rollback()
        
    def insert_individual(self, individual):
        session = self.scripts_session
        script_name = self.script_name
        ind_table = self.ind_table
        
        #print ind_table.c, "***************"
        #time.sleep(20)
        while True:
            try:    
                session.execute(ind_table.insert(), individual)
                """ I don't have the id of the individual to be inserted that is why I use a separate function """
                break
            except:
                print script_name, ": exception in insert individual at line", sys.exc_traceback.tb_lineno
                print sys.exc_info()[:-1]
                session.rollback()

    def pickle_individual(self, individual):
        data_structure = self.data_structure
        for key in individual.keys():
            if key in data_structure.keys():
                if data_structure[key] == constants.OBJ:
                    if individual[key] != None:
                        individual[key] = cPickle.dumps(individual[key], constants.PICKLE_PROTOCOL)

    def count_individuals(self, where=None):
        session = self.scripts_session
        ind_table = self.ind_table
        
        while True:
            try:
                query = select([func.count(ind_table.c.id)])
                if where:
                    query = self.process_where(query, ind_table, where)
                no_individuals = session.execute(query).scalar()
                #print "no_inds:", no_individuals
                break
            except:
                print "I am in count_individuals exception"
                print sys.exc_info()
                #print traceback.
                time.sleep(constants.POLLING_DELAY)
                session.rollback()
        #session.commit()
        return no_individuals
    
    def process_where(self, query, ind_table, where_tuple):
        """Process where clause.
        
        A helper method to process the where clause.
         
        """
        for item in where_tuple:
            if type(item[2]) is types.StringType:
                #TODO: convert to % format 
                #cond = "ind_table.c.%s%s'%s'" %(item[0], item[1], item[2])
                cond = "ind_table.c." + item[0] + item[1] + "\'" + item[2] + "\'"
                query = query.where(eval(cond))
            else:
                cond = "ind_table.c."+item[0]+item[1]+str(item[2])
                query = query.where(eval(cond))
        return query
    
    def process_live_state(self, where, at_time):
        if where == None: where = ()
        if not at_time:
            temp = list(where)
            temp = [(constants.LIFE_STATE, constants.EQUAL, constants.ALIVE)] + temp
            where = tuple(temp)
        else:
            self.wait_until(at_time)    
            temp = list(where)
            temp = [(constants.BIRTH_TS, constants.SMALLER, at_time), 
                    (constants.DEATH_TS, constants.GREATER_EQUAL, at_time)] + temp
            where = tuple(temp)   
            #print where     
        return where
    
    def find_individuals(self, select, where=(), count=None, order_by=None, 
                         exclusive_use=False, query_type=None):
        print self.script_name, "in get individuals"              
        #TODO: Check callers and decide for the pickling of returning inds
        session = ScriptsSession()
        select_tuple = select
        ind_table = self.ind_table
        script_settings = self.script_settings    
        script_name = self.script_name
        
        while True:
            try:
                query = ind_table.select()
                query = self.process_where(query, ind_table, where)
    
                cur_time = time.time()
                
                if exclusive_use == True:
                    """ PROCESSING TIMEOUT AND EXCLUSIVE_USE """
                    query = query.where(or_(eval("ind_table.c." + script_name + "==" + str(None)),
                                            eval("ind_table.c." + script_name + "<=" + str(cur_time - LooperService.TIME_OUT))
                                        )
                            )
                            
                """ PROCESSING ORDER BY CLAUSE """       
                if order_by != None:
                    query = query.order_by(eval("ind_table.c." + order_by[0] + "." + order_by[1]))
                else:
                    """ Check Database compatibility """
                    query = query.order_by(func.random())
                
                """ PROCESSING COUNT """
                if count != None:
                    query = query.limit(count)
                
                results = session.execute(query).fetchall()
                
                #print "The len of results", len(results)
                                
                pop_settings = self.get_population_obj()
                script_obj = self.get_script_obj()
                    
                """ POTENTIAL BOTTLENECK """
                if pop_settings.is_stopped:
                    return None      
                
                if script_obj.is_stopped:
                    raise exceptions.ScriptStoppedException("Script: '%s' has been stopped!!!" %script_name)
          
                if count == None or len(results) == count:
                    ind_list = []
                    for row_obj in results[:]:
                        temp = {}
                        temp['id'] = row_obj['id']
                        for select_item in select_tuple:
                            value = row_obj[select_item]
                            if select_item in self.data_structure.keys() and\
                               self.data_structure[select_item] == constants.OBJ and\
                               row_obj[select_item]:
                                temp[select_item] = cPickle.loads(str(row_obj[select_item]))
                            else:
                                temp[select_item] = row_obj[select_item]
                                
                        ind_list.append(temp)
                        if exclusive_use == True:
                            session.execute(ind_table.update().where(eval("ind_table.c." + script_name + " == " + str(row_obj['id']))), 
                                                                                            {script_name : time.time()})                    
                    print "Get Individuals took", time.time() - cur_time, "seconds..."
                    session.commit()                                        
                    
                    if query_type == LooperService.TS_QUERY:
                        return ind_list
                    else: return cPickle.dumps(ind_list, constants.PICKLE_PROTOCOL)
                
                time.sleep(constants.POLLING_DELAY)
            except exceptions.ScriptStoppedException, (instance):
                print instance.parameter
                raise instance
            except:
                print script_name, ": exception in get individuals at line:", sys.exc_traceback.tb_lineno
                print sys.exc_info()[1]
                time.sleep(constants.POLLING_DELAY)
                session.rollback()     
        return None

    def wait_until(self, at_time):
        print "I am in wait until"
        while True:
            script_obj = self.get_script_obj()
            
            if script_obj.is_stopped: 
                raise exceptions.ScriptStoppedException("Script: '%s' has been stopped!!!" %script_obj.name)
            
            no_inds = self.count_individuals()
            if no_inds >= at_time: 
                break
            time.sleep(constants.POLLING_DELAY*3)
            
class PopService(IndividualService):
    def on_connect(self):
        IndividualService.on_connect(self)
        print "Somebody connected in PopService"

    def on_disconnect(self):
        pass
        
    def exposed_initialize(self, schema_name, pop_name, script_name):
        IndividualService.initialize(self, schema_name, pop_name, script_name)
    
    def exposed_get_individuals(self, **kwargs):
        #TODO: implement it
        return self.find_individuals(**kwargs)
    
    def exposed_get_live_individuals(self, **kwargs):
        """Get live individuals.
        
        Exposed method to get live individuals.
        
        """
        where, at_time = None, None
        if "at_time" in kwargs.keys(): at_time = kwargs['at_time'] 
        if "where" in kwargs.keys(): where = kwargs['where']     
        kwargs["where"] = self.process_live_state(where, at_time)
        #TODO: Decide where to pickle inds.
        #TODO: Check the keyword unwrapping.
        del kwargs['at_time']
        return self.find_individuals(**kwargs)
    
    def exposed_count_live_individuals(self, where=(), at_time=None):
        """Count live individuals.
        
        Exposed method to count live individuals.
        
        """
        try:
            where = self.process_live_state(where, at_time)
            return self.count_individuals(where)
        except:
            traceback.print_tb(sys.exc_info()[2], file=sys.stdout)            
        
    def exposed_count_individuals(self):
        """ Count individuals.
        
        Exposed method to count all individuals in the current state.
        
        """
        return self.count_individuals()
    
    
class LooperService(IndividualService):
    TIME_OUT = 600 #IN TERMS OF SECONDS 
    TS_QUERY = "timeStampQuery"
    
    def on_connect(self):
        IndividualService.on_connect(self)
        print "Somebody connected in LooperService"
           
    def on_disconnect(self):
        pass  
                    
    def get_script_id(self, script_name, pop_name, schema_name):
        session = self.settings_session
        script_id = session.query(ScriptSettings.id).\
                            join(PopulationSettings).\
                            join(Schema).\
                            filter(ScriptSettings.name == script_name).\
                            filter(PopulationSettings.name == pop_name).\
                            filter(Schema.name == schema_name).scalar()
        return script_id    
    
    def exposed_initialize(self, schema_name, pop_name, script_name):
        IndividualService.initialize(self, schema_name, pop_name, script_name)
    
    def exposed_has_script_input(self):
        session = self.settings_session
        schema_name = self.schema_name
        pop_name = self.pop_name
        script_name = self.script_name
        script_select = session.query(ScriptSettings.cond_select).\
                            join(PopulationSettings).\
                            join(Schema).\
                            filter(PopulationSettings.name == pop_name).\
                            filter(Schema.name == schema_name).\
                            filter(ScriptSettings.name == script_name).scalar()
        if script_select: return True
        return False

    def exposed_get_global_data(self):
        pop_settings = self.get_population_obj()
        return cPickle.dumps(pop_settings.global_data, constants.PICKLE_PROTOCOL)
        
    def exposed_get_individuals(self):
        """Get all the individuals in the current database.
        
        Exposed method to get all the individuals.
        
        """
        script_obj = self.get_script_obj()
        return self.find_individuals(script_obj.cond_select, 
                                    script_obj.cond_where, 
                                    script_obj.input_size, 
                                    script_obj.order_by, 
                                    script_obj.exclusive_use
                )    

    def exposed_get_live_individuals(self, at_time=None):
        """Get live individuals.
        
        Exposed method to get live individuals.
        
        """        
        script_obj = self.get_script_obj()        
        where = self.process_live_state(script_obj.cond_where, at_time)
        return self.find_individuals(script_obj.cond_select, 
                                    where, 
                                    script_obj.input_size, 
                                    script_obj.order_by, 
                                    script_obj.exclusive_use
                )    
    
    def exposed_set_individuals(self, raw_individuals):
        print self.script_name, "in set individuals"
        session = self.scripts_session
        pop_settings = self.pop_settings
        script_name = self.script_name
        
        cur_time = time.time()
        individuals = cPickle.loads(raw_individuals)
        
        while True:
            try:
                no_individuals = self.count_individuals()

                print "total count:", script_name, no_individuals
                for individual in individuals:
                    self.pickle_individual(individual) # pickles only the object attributes of an individual  
                    
                    if 'id' in individual:
                        
                        individual[script_name+"_ts"] = no_individuals    
                        
                        if constants.LIFE_STATE in individual and\
                        individual[constants.LIFE_STATE] == constants.DEAD:
                            individual[constants.DEATH_TS] = no_individuals
                        
                        self.update_individual(individual)
                        
                    else:
                        """ INSERTING NEW INDIVIDUALS TO DATABASE """                      
                        individual[constants.BIRTH_TS] = no_individuals
                        individual[constants.DEATH_TS] = constants.INF
                        individual[constants.LIFE_STATE] = constants.ALIVE
                        self.insert_individual(individual)                          
                
                #print no_individuals, self.max_individuals
                if no_individuals + len(individuals) > self.max_individuals:               
                    pop_settings.is_stopped = True
                    self.settings_session.commit()   
                print "it took", time.time() - cur_time, "seconds..."    
                session.commit()    
                break
            except:
                print script_name, ": exception in set individuals", "at line", sys.exc_traceback.tb_lineno
                #time.sleep(5) 
                print sys.exc_info()
                session.rollback()
        print script_name, "in set individuals took ", time.time() - cur_time, "seconds..." 
                                
    def exposed_count_live_individuals(self, where=(), at_time=None):
        where = self.process_live_state(where, at_time)
        return self.count_individuals(where)
        
    def exposed_count_individuals(self, where=None):
        return self.count_individuals(where)

services = [{"service_name": ConfigService,
             "service_port": constants.CONFIG_SRVC_PORT
            },
            {"service_name": FrontendService,
             "service_port": constants.FRONTEND_SRVC_PORT
            },
            {"service_name": PopService,
             "service_port": constants.POP_SRVC_PORT
            },
            {"service_name": LooperService,
             "service_port": constants.LOOPER_SRVC_PORT
            }
]
    

def _start_rpc_service(service, _port):
    my_server = ThreadedServer(service, port=_port, auto_register=False)
    my_server.start()
    
def start_services():
    for service in services:
        t = Thread(target=_start_rpc_service, args=(service["service_name"], service["service_port"]))
        t.start()

"""
settings_engine = create_engine("sqlite:///database/config.db", echo=False, pool_size=50)
scripts_engine = create_engine("sqlite:///database/pop.db", echo=False, pool_size=50)
SettingsSession = sessionmaker(settings_engine)
ScriptsSession = sessionmaker(scripts_engine)     
"""

settings_engine = None
scripts_engine = None
SettingsSession = None
ScriptsSession = None

def reset_databases():
    metadata = MetaData()
    metadata.reflect(scripts_engine)
    metadata.drop_all(scripts_engine)    
    Base.metadata.drop_all(settings_engine)
    Base.metadata.create_all(settings_engine)

def initialize_databases():
    global settings_engine, scripts_engine, SettingsSession, ScriptsSession
    config_db = "sqlite:///%s/config.db" %constants.DATABASE_DIR
    pop_db = "sqlite:///%s/pop.db" %constants.DATABASE_DIR    
    settings_engine = create_engine(config_db, echo=False, pool_size=50)
    scripts_engine = create_engine(pop_db, echo=False, pool_size=50)
    SettingsSession = sessionmaker(settings_engine)
    ScriptsSession = sessionmaker(scripts_engine)     

def main():
    initialize_databases()
    reset_databases()
    start_services()
    try:
        raw_input()
    except:
        print ""
    finally:
        sys.exit()

if __name__ == "__main__":
    main()
